﻿using Microsoft.Practices.Prism.StoreApps;
using Microsoft.Practices.Prism.StoreApps.Interfaces;
using Newtonsoft.Json;
using Sumc.Client.ModernUI.Common;
using Sumc.Client.ModernUI.Extensions;
using Sumc.Client.ModernUI.Helpers;
using Sumc.Client.ModernUI.Models;
using Sumc.Client.ModernUI.Services;
using Sumc.Models.Responses;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Navigation;

namespace Sumc.Client.ModernUI.ViewModels
{
    public class SchedulePageViewModel : BaseViewModel
    {
        private string errorMessage;
        private bool isFound;
        private DelegateCommand saveSchedule;
        private DelegateCommand<int> loadArrivalTimes;
        private DelegateCommand pinTile;
        private DelegateCommand unpinTile;
        private DelegateCommand openRightTimes;
        private DelegateCommand<string> goToScheduleInformation;
        private bool isBottomAppBarSticky;
        private bool isBottomAppBarOpened;
        private bool isPinnedTile;
        private string rightTimeUrl;
        private TransportType transportType;
        private string route;
        private string title;
        private Stop stop;
        private ObservableCollection<string> arrivalTimes;
        private ObservableCollection<ExtendedStop> stops;
        private readonly IScheduleRepository scheduleRepository;
        private readonly ISecondaryTileService secondaryTileService;
        private readonly ISessionStateService sessionStateService;
        private readonly IShareService shareService;
        private readonly ISavePickerService savePickerService;

        public SchedulePageViewModel(INavigationService navigationService,
            IAlertMessageService alertMessageService,
            IScheduleRepository scheduleRepository,
            IShareService shareService,
            ISavePickerService savePickerService,
            ISecondaryTileService secondaryTileService,
            ISessionStateService sessionStateService,
            ISearchPaneService searchPaneService,
            IFlyoutService flyoutService)
            : base(navigationService,
            alertMessageService,
            flyoutService,
            searchPaneService)
        {
            this.sessionStateService = sessionStateService;
            this.secondaryTileService = secondaryTileService;
            this.scheduleRepository = scheduleRepository;
            this.shareService = shareService;
            this.savePickerService = savePickerService;
        }

        public DelegateCommand SaveSchedule
        {
            get
            {
                if (this.saveSchedule == null)
                {
                    this.saveSchedule = new DelegateCommand(this.SaveScheduleAsFile);
                }

                return this.saveSchedule;
            }
        }

        public DelegateCommand<int> LoadArrivalTimes
        {
            get
            {
                if (this.loadArrivalTimes == null)
                {
                    this.loadArrivalTimes = new DelegateCommand<int>(this.LoadArrivalTimesHandler);
                }

                return this.loadArrivalTimes;
            }
        }

        public DelegateCommand<string> GoToScheduleInformation
        {
            get
            {
                if (this.goToScheduleInformation == null)
                {
                    this.goToScheduleInformation = new DelegateCommand<string>(this.LoadScheduleInformationHandler);
                }

                return this.goToScheduleInformation;
            }
        }

        public DelegateCommand UnpinTile
        {
            get
            {
                if (this.unpinTile == null)
                {
                    this.unpinTile = new DelegateCommand(this.UnpinTileHandler);
                }

                return this.unpinTile;
            }
        }

        public DelegateCommand PinTile
        {
            get
            {
                if (this.pinTile == null)
                {
                    this.pinTile = new DelegateCommand(this.PinTileHandler);
                }

                return this.pinTile;
            }
        }

        public DelegateCommand OpenRightTimes
        {
            get
            {
                if (this.openRightTimes == null)
                {
                    this.openRightTimes = new DelegateCommand(this.OpenScheduleRightTimeInformation);
                }

                return this.openRightTimes;
            }
        }

        public string ErrorMessage
        {
            get
            {
                return this.errorMessage;
            }

            set
            {
                this.SetProperty(ref this.errorMessage, value);
            }
        }

        public bool IsFound
        {
            get
            {
                return this.isFound;
            }

            set
            {
                this.SetProperty(ref this.isFound, value);
            }
        }

        public bool IsBottomAppBarOpened
        {
            get
            {
                return this.isBottomAppBarOpened;
            }

            set
            {
                this.SetProperty(ref this.isBottomAppBarOpened, value);
            }
        }

        public bool IsBottomAppBarSticky
        {
            get
            {
                return this.isBottomAppBarSticky;
            }

            set
            {
                this.SetProperty(ref this.isBottomAppBarSticky, value);
            }
        }

        public TransportType TransportType
        {
            get
            {
                return this.transportType;
            }

            set
            {
                this.SetProperty(ref this.transportType, value);
            }
        }

        public bool IsPinnedTile
        {
            get
            {
                this.IsPinnedTileCheck();
                return this.isPinnedTile;
            }

            set
            {
                this.SetProperty(ref this.isPinnedTile, value);
            }
        }

        public Stop Stop
        {
            get
            {
                return this.stop;
            }

            set
            {
                this.SetProperty(ref this.stop, value);
            }
        }

        public string Route
        {
            get
            {
                return this.route;
            }

            set
            {
                this.SetProperty(ref this.route, value);
            }
        }

        public string Title
        {
            get
            {
                return this.title;
            }

            set
            {
                this.SetProperty(ref this.title, value);
            }
        }

        public ObservableCollection<ExtendedStop> Stops
        {
            get
            {
                return this.stops;
            }

            set
            {
                this.SetProperty(ref this.stops, value);
            }
        }

        public ObservableCollection<string> ArrivalTimes
        {
            get
            {
                return this.arrivalTimes;
            }

            set
            {
                this.SetProperty(ref this.arrivalTimes, value);
            }
        }

        private async void SaveScheduleAsFile()
        {
            var name = this.GetTitle();
            var text = await this.GetScheduleDataAsString();
            this.savePickerService.SaveToFile(text, name);
        }

        private async void UnpinTileHandler()
        {
            this.isBottomAppBarOpened = true;
            this.IsBottomAppBarSticky = true;
            await this.secondaryTileService.UnpinTile(this.GetTileId());
            this.IsBottomAppBarSticky = false;
            this.IsBottomAppBarOpened = false;
            this.OnPropertyChanged("IsPinnedTile");
        }

        private async void PinTileHandler()
        {
            this.IsBottomAppBarOpened = true;
            this.IsBottomAppBarSticky = true;
            var tileId = this.GetTileId();
            var name = this.GetTitle();
            var isPinned = await this.secondaryTileService.PinWideSecondaryTile(tileId, name, name, this.rightTimeUrl);
            if (isPinned)
            {
                var urlString = string.Format("{0}Tiles?tileId={1}", Constants.BaseSumcApiUrl, this.rightTimeUrl);
                var url = new Uri(urlString);
                this.secondaryTileService.Update(tileId, url);
            }

            this.IsBottomAppBarOpened = false;
            this.IsBottomAppBarSticky = false;
            this.OnPropertyChanged("IsPinnedTile");
        }

        private void IsPinnedTileCheck()
        {
            this.IsPinnedTile = this.secondaryTileService.SecondaryTileExists(this.GetTileId());
        }

        private string GetTileId()
        {
            var builder = new StringBuilder(50);
            builder.Append(this.title);
            builder.Append(this.transportType);
            if (this.Stop != null)
            {
                builder.Append(this.Stop.Number);
            }

            return builder.ToString();
        }

        private void LoadArrivalTimesHandler(int hour)
        {
            this.LoadData(async () =>
            {
                var dateTimeNow = DateTime.Now;
                var desiredDateTime = new DateTime(dateTimeNow.Year, dateTimeNow.Month, dateTimeNow.Day, hour, 0, 0);
                var arrivalTimes = await this.scheduleRepository.GetTransportSchedule(this.rightTimeUrl, desiredDateTime);
                this.ArrivalTimes.AddRange(arrivalTimes);
                var key = this.GetTileId();
                this.sessionStateService.SessionState[key] = this.arrivalTimes;
            });
        }

        private void LoadScheduleInformationHandler(string scheduleUrl)
        {
            this.shareService.Remove();
            this.LoadData(async () =>
            {
                await this.LoadScheduleInformation(scheduleUrl);
            });
        }

        private async Task LoadScheduleInformation(string scheduleUrl)
        {
            var schedule = await this.scheduleRepository.GetTransportSchedule(scheduleUrl);
            this.SetScheduleInformation(schedule);
        }

        private async Task LoadScheduleInformation(ScheduleInformation scheduleInformation)
        {
            var schedule = await this.scheduleRepository.GetTransportSchedule(scheduleInformation.TransportType,
                scheduleInformation.Lid,
                scheduleInformation.Rid,
                scheduleInformation.StopValue);
            this.SetScheduleInformation(schedule);
        }

        private void SetScheduleInformation(Schedule schedule)
        {
            this.IsFound = schedule.IsFound;
            if (schedule.IsFound)
            {
                this.ArrivalTimes = new ObservableCollection<string>();
                this.ArrivalTimes.FillCollection(schedule.ArrivalsTimes);
                this.Route = schedule.Route;
                this.Stop = schedule.Stop;
                this.Stops = new ObservableCollection<ExtendedStop>();
                this.Stops.FillCollection(schedule.Stops);
                this.Title = schedule.Title;
                this.TransportType = schedule.VehicleType;
                this.rightTimeUrl = schedule.RightTimeUrl;

                this.SetScheduleInformationForSharing();
            }
            else
            {
                this.ErrorMessage = schedule.Error;
            }
        }

        private async void SetScheduleInformationForSharing()
        {
            this.shareService.Initialize();
            this.shareService.Title = this.GetTitle();
            this.shareService.Data = await this.GetScheduleDataAsString();
        }

        private string GetTitle()
        {
            switch (transportType)
            {
                case TransportType.Bus:
                    return "Автобус " + this.Title;
                case TransportType.Trolley:
                    return "Тролей " + this.Title;
                case TransportType.Tram:
                    return "Трамвай " + this.Title;
                default:
                    return null;
            }
        }

        private Task<string> GetScheduleDataAsString()
        {
            return Task.Run<string>(() =>
            {
                var builder = new StringBuilder();
                builder.Append(this.GetTitle());
                builder.AppendLine();
                builder.Append("Спирка: ");
                builder.Append(this.Stop.Name);
                builder.Append(" (");
                builder.Append(this.Stop.Number);
                builder.AppendLine(")");
                builder.AppendLine("Разписание: ");
                builder.AppendLine(string.Join(Environment.NewLine, this.ArrivalTimes));
                return builder.ToString();
            });
        }

        private void OpenScheduleRightTimeInformation()
        {
            this.flyoutService.ShowFlyout(Constants.ScheduleRightTimeFlyout, this.rightTimeUrl, null);
        }

        public override void OnNavigatedTo(object navigationParameter, NavigationMode navigationMode, Dictionary<string, object> viewModelState)
        {
            base.OnNavigatedTo(navigationParameter, navigationMode, viewModelState);
            var scheduleString = navigationParameter as string;
            this.LoadData(async () =>
            {
                if (scheduleString[0] == '{')
                {
                    var scheduleInformation = await JsonConvert.DeserializeObjectAsync<ScheduleInformation>(scheduleString);
                    await this.LoadScheduleInformation(scheduleInformation);
                }
                else
                {
                    await this.LoadScheduleInformation(scheduleString);
                }
            });
        }

        public override void OnNavigatedFrom(Dictionary<string, object> viewModelState, bool suspending)
        {
            base.OnNavigatedFrom(viewModelState, suspending);
            this.shareService.Remove();
        }
    }
}
